#include <iostream>

#include <nemesis.h>
#include <nemesis.dbms.h>
#include <nemesis.app.h>

#include <nemesis.dbms.oracle.Database.h>

class TestNull : public Application {
public:
   TestNull ();
   
private:
   nemesis::dbms::oracle::Database* a_db;

   void initialize () throw (RuntimeException); 
   void run () throw (RuntimeException);      
   void insert (Connection*) throw ();
   void update (Connection*) throw ();
};

using namespace std;

int main (int argc, const char** argv)
{
   CommandLine& commandLine (CommandLine::instanciate ());
   TestNull testNull;

   try {
      commandLine.initialize (argv, argc);
      commandLine.verify ();

      Logger::setLevel (Logger::Debug); 
      Logger::initialize ("db_blob", new TraceWriter ("db_blob.trace", 1024 * 1024));
 
      testNull.start ();
   }
   catch (Exception& ex) {
      cout << ex.asString () << endl;
   }
   
   return 0;
}

TestNull::TestNull () : 
   Application ("db_blob", "Probador de BLOBs", "1.0") 
{
   CommandLine& cl (CommandLine::instanciate ());
      
   cl.add ("user", CommandLine::Argument::Mandatory, "Nombre del usuario");
   cl.add ("password", CommandLine::Argument::Mandatory, "Clave del usuario");
   cl.add ("db", CommandLine::Argument::Optional, "Nombre de la base de datos");
}

void TestNull::initialize ()
   throw (RuntimeException)
{
   a_db = new nemesis::dbms::oracle::Database (CommandLine::instanciate ().getValue ("db"));   
}

void TestNull::run () 
   throw (RuntimeException)
{
   LOGMETHOD (TraceMethod tm ("TestNull", "run", FILE_LOCATION)); 

   CommandLine& cl (CommandLine::instanciate ());
   
   Statement* create;
   Statement* select;
  
   dbms::Integer n (true);
   dbms::LongBlock data (true);

   dbms::ResultCode resultCode;

   try {
      Connection* connection = a_db->createConnection ("prueba01", cl.getValue ("user"), cl.getValue ("password"));
    
      Guard guard (connection);

      try {
         create = a_db->createStatement ("create", "create table nemesis_db_blob (xx integer, yy blob not null)");
         resultCode = connection->execute (create);
         cout << "Creando: " << resultCode.asString () << endl << endl;
      }
      catch (dbms::DatabaseException& edb) {
         connection->resetRollbackPending ();
         edb.trace ();
         
         try {
            create = a_db->createStatement ("delete", "delete from nemesis_db_blob");
            resultCode = connection->execute (create);
         }
         catch (dbms::DatabaseException& edb) {
            throw RuntimeException (edb);
         }
      }
      
      insert (connection);
      update (connection);
      
      select = a_db->createStatement ("select", "select xx,yy from nemesis_db_blob order by 1");
      select->bindOutput ("XX", n);
      select->bindOutput ("YY", data);

      cout << endl << " --- Leyendo ---" << endl;
      resultCode = connection->execute (select);
                        
      if (resultCode.successful () == true) {
         while (select->fetch () == true) {
            if (n.isNull () == true)
               cout << "<null>";
            else
               cout << n;

            cout << " | YY: ";
            
            if (data.isNull () == true) 
               cout << "<null>";
            else
               cout << nemesis::functions::asString (data.getValue ());
               
            cout << endl;
         }         
      }
      else
         cout << resultCode.asString () << endl << endl;
   }
   catch (dbms::DatabaseException& edb) {
      throw RuntimeException (edb); 
   }
}

void TestNull::insert (Connection* connection) 
   throw ()
{
   Statement* insert (NULL);
   dbms::Integer n (true);
   dbms::LongBlock data (true);
   dbms::ResultCode resultCode;
   
   try {
      connection->execute (
         a_db->createStatement ("insert0", "insert into nemesis_db_blob (xx,yy) values (5, '1234567890')")
      );

      connection->execute (
         a_db->createStatement ("insert1", "insert into nemesis_db_blob (xx,yy) values (6, 'ABCDEFFF0112')")
      );

      connection->execute (
         a_db->createStatement ("insert2", "insert into nemesis_db_blob (xx,yy) values (0, empty_blob ())")
      );
      connection->execute (
         a_db->createStatement ("insert1000", "insert into nemesis_db_blob (xx,yy) values (1000, empty_blob ())")
      );

      insert = a_db->createStatement ("insert3/1", "insert into nemesis_db_blob (xx, yy) values (:xx, empty_blob ())");
      insert->bindInput ("xx", n);
      n = 3;
      resultCode = connection->execute (insert);
      
      insert =  a_db->createStatement ("insert4", "insert into nemesis_db_blob (xx, yy) values (:xx,:yy)");
      insert->bindInput ("xx", n);
      insert->bindInput ("yy", data);
      n = 4;
      data = DataBlock ("1234", nemesis_strlen ("1234"));
      resultCode = connection->execute (insert);
      cout << resultCode.asString () << endl << endl;
   }
   catch (Exception& ex) {
      ex.trace ();
      if (insert != NULL)
         cout << insert->getName () << ": ";
      cout << ex.asString () << endl << endl;
   }
}

void TestNull::update (Connection* connection) 
   throw ()
{
   Statement* select (NULL);
   Statement* update (NULL);
   dbms::Integer n (true);
   dbms::LongBlock data (true);
   dbms::ResultCode resultCode;
   const dbms::OutputBind* yy;
   
   try {
      select = a_db->createStatement ("selectx", "select yy from nemesis_db_blob where xx=0 for update");
      select->bindOutput ("YY", data);
      resultCode = connection->execute (select);
      cout << "selectx Len: " << data.getValue ().getSize () << " | " << resultCode.asString () << endl << endl;

      update = a_db->createStatement ("update3", "select yy from nemesis_db_blob where xx=:xx");
      yy = update->bindOutput ("yy", data);
      update->bindInput ("xx", n);
      n = 3;
      resultCode = connection->execute (update);
      cout << resultCode.asString () << endl << endl;     
      
      data = DataBlock ("123", nemesis_strlen ("123"));
      yy->write ();

      update = a_db->createStatement ("Update 0", "select yy from nemesis_db_blob where xx=0");
      yy = update->bindOutput ("YY", data);      
      resultCode = connection->execute (update);
      cout << "Update 0: " << resultCode.asString () << endl << endl;
      
      DataBlock dataBlock (true);            
      for (int i = 0; i < 512; i ++)
         dataBlock += char (i % 256);         
      data = dataBlock;
      yy->write ();

      update = a_db->createStatement ("Update 1000", "select yy from nemesis_db_blob where xx=:xx");
      yy = update->bindOutput ("YY", data);      
      update->bindInput ("xx", n);
      n = 1000;
      resultCode = connection->execute (update);
      cout << "Update 1000: " << resultCode.asString () << endl << endl;
      
      data = DataBlock ("87654321", nemesis_strlen ("87654321"));
      yy->write ();
   }
   catch (Exception& ex) {
      ex.trace ();
      if (update != NULL)
         cout << update->getName () << ": ";
      cout << ex.asString () << endl << endl;
   }
}
